package msc.ai.mas.creditcard;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;

public class OnlineStoreAgent extends Agent
{
	private HashMap<String,SalesItem> salesItemList;
	private ArrayList<AID> pGatewayList = new ArrayList<AID>();
	
	private OnlineStoreGui storeGui;
	
	protected void setup() 
	{

		salesItemList = new HashMap<String, SalesItem>();

		storeGui = new OnlineStoreGui(this);
		storeGui.showGui();

		DFAgentDescription dfd = new DFAgentDescription();
		dfd.setName(getAID());
		ServiceDescription sd = new ServiceDescription();
		sd.setType("online-store");
		sd.setName("online-store-trading");
		dfd.addServices(sd);
		try 
		{
			DFService.register(this, dfd);
		}
		catch (FIPAException fe) 
		{
			fe.printStackTrace();
		}	
		
		addBehaviour(new ItemOfferBehavior());
		
		addBehaviour(new TickerBehaviour(this, 30000) 
		{
		      protected void onTick() 
		      {
		        
		        DFAgentDescription template = new DFAgentDescription();
		        ServiceDescription sd = new ServiceDescription();
		        sd.setType("payment-gateway");
		        template.addServices(sd);
		        try 
		        {
		          DFAgentDescription[] result = DFService.search(myAgent, template);
		          pGatewayList.clear();
		          for (int i = 0; i < result.length; ++i) 
		          {
		        	  pGatewayList.add(result[i].getName());  
		          }
		        }
		        catch (FIPAException fe) 
		        {
		          fe.printStackTrace();
		        }
		      }
	    } );
		
		
	}
	
	protected void takeDown() 
	{
		
		try 
		{
			DFService.deregister(this);
		}
		catch (FIPAException fe) 
		{
			fe.printStackTrace();
		}
		
		this.storeGui.dispose();
		
		System.out.println("Online-Store-Agent "+getAID().getName()+" terminating.");
	}
	
	
	public  HashMap<String, SalesItem> getSellItemList()
	{
		return salesItemList;
	}

	public void addItem(final SalesItem item)
	{
		addBehaviour(new OneShotBehaviour() 
		{			
		
			@Override
			public void action()
			{
				System.out.println( getAID().getLocalName() + ": " + item.getItemQty() + " items of Item " + item.getItemName() + " priced at " + item.getItemPrice() + " added" );
				
				if (salesItemList == null ) salesItemList =  new HashMap<String, SalesItem>();
				
				salesItemList.put(item.getItemName(),item);
				
			}
			
		});
		
	}
	
	private class ItemOfferBehavior extends CyclicBehaviour 
	{
		public void action() 
		{
			MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.CFP);
			ACLMessage msg = myAgent.receive(mt);
			if (msg != null) 
			{

				String itemName = msg.getContent();
				ACLMessage reply = msg.createReply();

				SalesItem item = (SalesItem) salesItemList.get(itemName);
				if (item != null) 
				{
					if (item.getItemQty() <= 0)
					{
						reply.setPerformative(ACLMessage.REFUSE);
						reply.setContent("stock-over");
					}
					else
					{
						reply.setPerformative(ACLMessage.PROPOSE);
						try
						{
							reply.setContentObject(item);
							myAgent.addBehaviour(new ItemSalesBehavior(item));
							
						} catch (IOException e)
						{
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					
				}
				else 
				{	
					reply.setPerformative(ACLMessage.REFUSE);
					reply.setContent("not-available");
				}
				myAgent.send(reply);
			}
			else 
			{
				block();
			}
		}
	} // End of ItemOfferBehavior
	
	private class ItemSalesBehavior extends CyclicBehaviour
	{
		private SalesItem sItem;
		private BuyingItem bItem;
		public ItemSalesBehavior(SalesItem sItem)
		{
			this.sItem = sItem;
		} 
		
		@Override
		public void action()
		{
			// validate the the buying details and send purchase response.
			MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.ACCEPT_PROPOSAL);
			ACLMessage msg = myAgent.receive(mt);
			if (msg != null) 
			{
				try
				{
					System.out.println(getAID().getLocalName() + ": Proposal Accepted. Initiating sales process.");
					bItem = (BuyingItem )msg.getContentObject();
					
					myAgent.addBehaviour(new SalesProcessBehavior(msg, bItem,sItem));
					
				} catch (UnreadableException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
			else 
			{
				block();
			}
			
		}
		
	}
	
	private class SalesProcessBehavior extends Behaviour
	{
		private SalesItem sItem;
		private BuyingItem bItem;
		private int salesQty;
		private double totalPrice;
		private int step = 0;
		private ItemSalesBehavior itmBehavior;
		
		private ACLMessage shopperMsg;
		private MessageTemplate mt;
		
		public SalesProcessBehavior(ACLMessage apMsg, BuyingItem bItem,SalesItem sItem)
		{
			this.shopperMsg = apMsg;
			this.bItem = bItem;
			this.sItem = sItem;
			System.out.println(getAID().getLocalName() + ": Sales process initiated.");
		}

		@Override
		public void action()
		{
			// TODO Auto-generated method stub
			switch (step)
			{
				case 0:					
					// send payment gateway request
					// calc sales price and qty
					if (bItem.getItemQtyRequired() > sItem.getItemQty()) salesQty =sItem.getItemQty();
					else salesQty = bItem.getItemQtyRequired();
					
					totalPrice = salesQty * sItem.getItemPrice();
					
					System.out.println(getAID().getLocalName() + ": Sending Payment Gateway Authorization. Tot Price : " + totalPrice 
									+ ", Qty : " + salesQty + ", ItemPrice : " +  sItem.getItemPrice());
					
					BuyingItem finalBItem = new BuyingItem();
					
					finalBItem.setCreditCardNo(bItem.getCreditCardNo());
					finalBItem.setCreditCardType(bItem.getCreditCardType());
					finalBItem.setCreditCVVNo(bItem.getCreditCVVNo());
					finalBItem.setItemQtyRequired(salesQty);
					finalBItem.setItemSearching(bItem.getItemSearching());
					finalBItem.setMaxItemPrice(sItem.getItemPrice());
					
					
					try
					{
						ACLMessage req = new ACLMessage(ACLMessage.REQUEST);
						for (int i = 0; i < pGatewayList.size(); ++i) 
						{
							//TODO: Select only one???
							req.addReceiver((AID)pGatewayList.get(i));
						}
						req.setContentObject(finalBItem);
						req.setConversationId("cc-authorization");
						req.setReplyWith("request"+System.currentTimeMillis()); // Unique value
						myAgent.send(req);
				          
						mt = MessageTemplate.and(
								MessageTemplate.MatchConversationId("cc-authorization"),
								MessageTemplate.MatchInReplyTo(req.getReplyWith())
							);
						step = 1;
						
					}
					catch (Exception e) 
					{
						// TODO: handle exception
						e.printStackTrace();
						step = 2;
					}
					
					
					break;
					
				case 1:
					// wait for payment gateway reply
					// if replied ok, then update the inventory
					ACLMessage reply = myAgent.receive(mt);
					if (reply != null) 
					{
						System.out.println(getAID().getLocalName() + ": CC Auth Reply Recieved"); 
						
						// send the reply
						ACLMessage shpReply = shopperMsg.createReply();
						if (reply.getPerformative() == ACLMessage.INFORM)
						{
							salesItemList.get(sItem.getItemName()).sellItem(salesQty);
							shpReply.setPerformative(ACLMessage.INFORM);
							System.out.println(getAID().getLocalName() + ": " + bItem.getItemSearching() +" sold to agent "+ shopperMsg.getSender().getName());
						}
						else 
						{					
							System.out.println(getAID().getLocalName() + ": " + " Credit Card Authorization Failed.");
							shpReply.setPerformative(ACLMessage.FAILURE);
							shpReply.setContent(reply.getContent());
						}
						myAgent.send(shpReply);
						
					}
					
										
					step = 2;
					break;
					
				case 2:
					break;
				default:
					break;
			}
		}

		@Override
		public boolean done()
		{
			// TODO Auto-generated method stub
			if (step == 2) return true;
			return false;
		}
		
	}
	
	
	
	
}
